package cool.doudou.doudada.cipher.algorithm.util;

import cool.doudou.doudada.cipher.algorithm.sm.Cipher;
import cool.doudou.doudada.cipher.algorithm.sm.Sm2;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.springframework.util.ObjectUtils;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 国密SM2
 *
 * @author jiangcs
 * @since 2022/08/31
 */
public class Sm2Util {
    /**
     * 加密
     *
     * @param publicKey 公钥
     * @param plaintext 明文
     * @return 16进制加密字符串
     */
    public static String encrypt(String publicKey, String plaintext) {
        if (ObjectUtils.isEmpty(publicKey)) {
            return null;
        }
        if (ObjectUtils.isEmpty(plaintext)) {
            return null;
        }

        byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8);
        byte[] source = new byte[plaintextBytes.length];
        System.arraycopy(plaintextBytes, 0, source, 0, plaintextBytes.length);

        Cipher cipher = new Cipher();
        Sm2 sm2 = Sm2.Instance();
        ECPoint userKey = sm2.eccCurve.decodePoint(hexToByte(publicKey));
        ECPoint ecPointC1 = cipher.initEnc(sm2, userKey);
        cipher.encrypt(source);

        byte[] outBytes = new byte[32];
        cipher.doFinal(outBytes);
        return byteToHex(ecPointC1.getEncoded(false)) + byteToHex(outBytes) + byteToHex(source);
    }

    /**
     * 解密
     *
     * @param privateKey 私钥
     * @param ciphertext 密文
     * @return 解密字符串
     */
    public static String decrypt(String privateKey, String ciphertext) {
        if (ObjectUtils.isEmpty(privateKey)) {
            return null;
        }
        if (ObjectUtils.isEmpty(ciphertext)) {
            return null;
        }
        byte[] privateKeyBytes = hexToByte(privateKey);
        if (privateKeyBytes == null) {
            return null;
        }
        byte[] ciphertextBytes = hexToByte(ciphertext);
        if (ciphertextBytes == null) {
            return null;
        }
        // 加密字节数组转换为十六进制的字符串 长度变为encryptedData.length * 2
        /*
          分解加密字串 C1 | C2 | C3
          （C1 = C1标志位2位 + C1实体部分128位 = 130）
          （C3 = C3实体部分64位  = 64）
          （C2 = encryptedData.length * 2 - C1长度  - C2长度）
           byte[] c1Bytes = Util.hexToByte(data.substring(0,130));
           int c2Len = encryptedData.length - 97;
           byte[] c2 = Util.hexToByte(data.substring(130,130 + 2 * c2Len));
           byte[] c3 = Util.hexToByte(data.substring(130 + 2 * c2Len,194 + 2 * c2Len));
         */
        /*分解加密字串 C1 | C3 | C2
          （C1 = C1标志位2位 + C1实体部分128位 = 130）
          （C3 = C3实体部分64位  = 64）
          （C2 = encryptedData.length * 2 - C1长度  - C2长度）
         */
        byte[] c1Bytes = hexToByte(ciphertext.substring(0, 130));
        byte[] c3 = hexToByte(ciphertext.substring(130, 130 + 64));
        byte[] c2 = hexToByte(ciphertext.substring(194, 194 + 2 * (ciphertextBytes.length - 97)));
        if (c2 == null) {
            return null;
        }

        Sm2 sm2 = Sm2.Instance();
        BigInteger userD = new BigInteger(1, privateKeyBytes);

        // 通过C1实体字节来生成ECPoint
        ECPoint ecPointC1 = sm2.eccCurve.decodePoint(c1Bytes);
        Cipher cipher = new Cipher();
        cipher.initDec(userD, ecPointC1);
        cipher.decrypt(c2);
        cipher.doFinal(c3);
        // 解密结果
        return new String(c2);
    }

    // 生成随机秘钥对
    public static Map<String, String> generateKeyPair() {
        Sm2 sm2 = Sm2.Instance();
        AsymmetricCipherKeyPair key;
        while (true) {
            key = sm2.eccKeyPairGenerator.generateKeyPair();
            if (((ECPrivateKeyParameters) key.getPrivate()).getD().toByteArray().length == 32) {
                break;
            }
        }
        ECPrivateKeyParameters ecPrivateKeyParameters = (ECPrivateKeyParameters) key.getPrivate();
        ECPublicKeyParameters ecPublicKeyParameters = (ECPublicKeyParameters) key.getPublic();
        BigInteger privateKey = ecPrivateKeyParameters.getD();
        ECPoint publicKey = ecPublicKeyParameters.getQ();
        Map<String, String> result = new HashMap<>(2);
        result.put("publicKey", byteToHex(publicKey.getEncoded()));
        result.put("privateKey", byteToHex(privateKey.toByteArray()));
        return result;
    }

    /**
     * 字节数组转换为十六进制字符串
     *
     * @param bytes 需要转换的字节数组
     * @return String 十六进制字符串
     */
    private static String byteToHex(byte[] bytes) {
        if (bytes == null) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(b & 0xFF);
            if (hex.length() < 2) {
                hex = "0" + hex;
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    /**
     * 十六进制串转化为byte数组
     *
     * @return 字节数组
     */
    private static byte[] hexToByte(String hex) throws IllegalArgumentException {
        if (hex.length() % 2 != 0) {
            throw new IllegalArgumentException();
        }
        if (hex.length() < 1) {
            return null;
        } else {
            byte[] result = new byte[hex.length() / 2];
            int j = 0;
            for (int i = 0; i < hex.length(); i += 2) {
                result[j++] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
            }
            return result;
        }
    }
}
